#include "defaults.h"
#include "../platforms/platform.h"
#include "../stereokit.h"
#include "../shaders_builtin/shader_builtin.h"
#include "../asset_types/font.h"
#include "../asset_types/texture_.h"

#include <string.h>

namespace sk {

///////////////////////////////////////////

tex_t        sk_default_tex;
tex_t        sk_default_tex_black;
tex_t        sk_default_tex_gray;
tex_t        sk_default_tex_flat;
tex_t        sk_default_tex_rough;
tex_t        sk_default_tex_devtex;
tex_t        sk_default_tex_error;
tex_t        sk_default_cubemap;
mesh_t       sk_default_quad;
mesh_t       sk_default_screen_quad;
mesh_t       sk_default_sphere;
mesh_t       sk_default_cube;
shader_t     sk_default_shader;
shader_t     sk_default_shader_blit;
shader_t     sk_default_shader_pbr;
shader_t     sk_default_shader_pbr_clip;
shader_t     sk_default_shader_unlit;
shader_t     sk_default_shader_unlit_clip;
shader_t     sk_default_shader_lightmap;
shader_t     sk_default_shader_font;
shader_t     sk_default_shader_equirect;
shader_t     sk_default_shader_ui;
shader_t     sk_default_shader_ui_box;
shader_t     sk_default_shader_ui_quadrant;
shader_t     sk_default_shader_ui_aura;
shader_t     sk_default_shader_sky;
shader_t     sk_default_shader_lines;
material_t   sk_default_material;
material_t   sk_default_material_pbr;
material_t   sk_default_material_pbr_clip;
material_t   sk_default_material_unlit;
material_t   sk_default_material_unlit_clip;
material_t   sk_default_material_equirect;
material_t   sk_default_material_font;
material_t   sk_default_material_ui;
material_t   sk_default_material_ui_box;
material_t   sk_default_material_ui_quadrant;
material_t   sk_default_material_ui_aura;
font_t       sk_default_font;
text_style_t sk_default_text_style;
sound_t      sk_default_click;
sound_t      sk_default_unclick;
sound_t      sk_default_grab;
sound_t      sk_default_ungrab;

const spherical_harmonics_t sk_default_lighting = { {
	{ 0.74f,  0.74f,  0.73f},
	{ 0.24f,  0.25f,  0.26f},
	{ 0.09f,  0.09f,  0.09f},
	{ 0.05f,  0.05f,  0.06f},
	{-0.01f, -0.01f, -0.01f},
	{-0.03f, -0.03f, -0.03f},
	{ 0.00f,  0.00f,  0.00f},
	{-0.02f, -0.02f, -0.02f},
	{ 0.04f,  0.04f,  0.04f},
} };

///////////////////////////////////////////

tex_t defaults_texture(const char *id, color128 color) {
	tex_t result = tex_gen_color(color, 2, 2, tex_type_image_nomips, tex_format_rgba32_linear);
	tex_set_id(result, id);
	return result;
}

///////////////////////////////////////////

tex_t dev_texture(const char *id, color128 base_color, float contrast_boost) {
	tex_t result = tex_create();
	tex_set_id(result, id);

	const int32_t size          = 256;                 // Texture total size
	const int32_t slices        = 4;                   // Slice this up into 4x4 squares
	const int32_t slice_size    = size / slices;       // Size in px of each square
	const int32_t slice_half    = slice_size/2;        // precalculate half (for lines)
	const int32_t slice_quarter = slice_size/4;        // precalculate quarter (for lines)
	const int32_t checker_size  = size / (slices * 2); // Alternate core color in a checker pattern, with 2 checkers per slice

	vec3 lab = color_to_lab(base_color);
	color32 core_color  = color_to_32(base_color);
	color32 core_color2 = color_to_32(color_lab(lab.x * powf(0.9f,  contrast_boost), lab.y, lab.z, 1));
	color32 line_color  = color_to_32(color_lab(lab.x * powf(0.8f,  contrast_boost), lab.y, lab.z, 1));
	color32 line2_color = color_to_32(color_lab(lab.x * powf(0.75f, contrast_boost), lab.y, lab.z, 1));

	color32 *data = sk_malloc_t(color32, size * size);
	for (int32_t y = 0; y < size; y++) {
		int ydist  = abs(slice_half    - ((y + slice_half   ) % slice_size));
		int ydist2 = abs(slice_quarter - ((y + slice_quarter) % slice_half));

		for (int32_t x = 0; x < size; x++) {
			int xdist  = abs(slice_half    - ((x + slice_half   ) % slice_size));
			int xdist2 = abs(slice_quarter - ((x + slice_quarter) % slice_half));

			int32_t i = x + y * size;
			if      (xdist < 2 || ydist <2) data[i] = line_color;
			else if (xdist2< 1 || ydist2<1) data[i] = line2_color;
			else                            data[i] = ((x/checker_size) + (y/checker_size)) %2 == 0 ? core_color : core_color2;
		}
	}

	tex_set_colors(result, size, size, data);
	sk_free(data);

	return result;
}

///////////////////////////////////////////

bool defaults_init() {
	// Textures
	sk_default_tex       = defaults_texture(default_id_tex,       {1,1,1,1}         );
	sk_default_tex_black = defaults_texture(default_id_tex_black, {0,0,0,1}         );
	sk_default_tex_gray  = defaults_texture(default_id_tex_gray,  {0.5f,0.5f,0.5f,1});
	sk_default_tex_flat  = defaults_texture(default_id_tex_flat,  {0.5f,0.5f,1,1}   ); // Default for normal maps
	sk_default_tex_rough = defaults_texture(default_id_tex_rough, {1,1,0,1}         ); // Default for metal/roughness maps

	sk_default_tex_devtex = dev_texture(default_id_tex_devtex, { 1,1,   1,   1 }, 1);
	sk_default_tex_error  = dev_texture(default_id_tex_error,  { 1,0.7f,0.7f,1 }, 1);

	if (sk_default_tex       == nullptr ||
		sk_default_tex_black == nullptr ||
		sk_default_tex_gray  == nullptr ||
		sk_default_tex_flat  == nullptr ||
		sk_default_tex_rough == nullptr ||
		sk_default_tex_devtex== nullptr ||
		sk_default_tex_error == nullptr)
		return false;

	tex_set_loading_fallback(sk_default_tex_devtex);
	tex_set_error_fallback  (sk_default_tex_error);

	// Cubemap
	spherical_harmonics_t lighting = sk_default_lighting;
	sh_brightness(lighting, 0.75f);
	sk_default_cubemap = tex_gen_cubemap_sh(lighting, 16, 0.3f);
	tex_set_id(sk_default_cubemap, default_id_cubemap);

	// Default quad mesh
	sk_default_quad = mesh_create();
	vert_t verts[4] = {
		{ vec3{-0.5f,-0.5f,0}, vec3{0,0,-1}, vec2{1,1}, color32{255,255,255,255} },
		{ vec3{ 0.5f,-0.5f,0}, vec3{0,0,-1}, vec2{0,1}, color32{255,255,255,255} },
		{ vec3{ 0.5f, 0.5f,0}, vec3{0,0,-1}, vec2{0,0}, color32{255,255,255,255} },
		{ vec3{-0.5f, 0.5f,0}, vec3{0,0,-1}, vec2{1,0}, color32{255,255,255,255} }, };
	vind_t inds[6] = { 2,1,0, 3,2,0 };
	mesh_set_data(sk_default_quad, verts, 4, inds, 6);
	
	// Default rendering quad
	sk_default_screen_quad = mesh_create();
	vert_t sq_verts[4] = {
		{ vec3{-1,-1,0}, vec3{0,0,1}, vec2{0,1}, color32{255,255,255,255} },
		{ vec3{ 1,-1,0}, vec3{0,0,1}, vec2{1,1}, color32{255,255,255,255} },
		{ vec3{ 1, 1,0}, vec3{0,0,1}, vec2{1,0}, color32{255,255,255,255} },
		{ vec3{-1, 1,0}, vec3{0,0,1}, vec2{0,0}, color32{255,255,255,255} }, };
	vind_t sq_inds[6] = { 0,1,2, 0,2,3 };
	mesh_set_data(sk_default_screen_quad, sq_verts, 4, sq_inds, 6);
	
	sk_default_cube   = mesh_gen_cube(vec3_one);
	sk_default_sphere = mesh_gen_sphere(1);

	mesh_set_id(sk_default_quad,        default_id_mesh_quad);
	mesh_set_id(sk_default_screen_quad, default_id_mesh_screen_quad);
	mesh_set_id(sk_default_cube,        default_id_mesh_cube);
	mesh_set_id(sk_default_sphere,      default_id_mesh_sphere);

	// Shaders
	int32_t size = 0;
	void*   data = nullptr;
#define SHADER_DECODE(shader_mem) { sk_free(data); data = unzip_malloc(shader_mem, sizeof(shader_mem), &size); }
	SHADER_DECODE(sks_shader_builtin_default_hlsl_zip    ); sk_default_shader             = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_blit_hlsl_zip       ); sk_default_shader_blit        = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_unlit_hlsl_zip      ); sk_default_shader_unlit       = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_unlit_clip_hlsl_zip ); sk_default_shader_unlit_clip  = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_lightmap_hlsl_zip   ); sk_default_shader_lightmap    = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_font_hlsl_zip       ); sk_default_shader_font        = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_equirect_hlsl_zip   ); sk_default_shader_equirect    = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_ui_hlsl_zip         ); sk_default_shader_ui          = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_ui_box_hlsl_zip     ); sk_default_shader_ui_box      = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_ui_quadrant_hlsl_zip); sk_default_shader_ui_quadrant = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_ui_aura_hlsl_zip    ); sk_default_shader_ui_aura     = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_skybox_hlsl_zip     ); sk_default_shader_sky         = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_lines_hlsl_zip      ); sk_default_shader_lines       = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_pbr_hlsl_zip        ); sk_default_shader_pbr         = shader_create_mem(data, size);
	SHADER_DECODE(sks_shader_builtin_pbr_clip_hlsl_zip   ); sk_default_shader_pbr_clip    = shader_create_mem(data, size);
	sk_free(data);
#undef SHADER_DECODE
	
	// Android seems to give us a hard time about this one, so let's fall
	// back at least somewhat gently.
	if (sk_default_shader && !sk_default_shader_pbr) {
		sk_default_shader_pbr = sk_default_shader;
		shader_addref(sk_default_shader);
	}
	if (sk_default_shader && !sk_default_shader_pbr_clip) {
		sk_default_shader_pbr_clip = sk_default_shader;
		shader_addref(sk_default_shader);
	}

	if (sk_default_shader             == nullptr ||
		sk_default_shader_blit        == nullptr ||
		sk_default_shader_pbr         == nullptr ||
		sk_default_shader_pbr_clip    == nullptr ||
		sk_default_shader_unlit       == nullptr ||
		sk_default_shader_unlit_clip  == nullptr ||
		sk_default_shader_lightmap    == nullptr ||
		sk_default_shader_font        == nullptr ||
		sk_default_shader_equirect    == nullptr ||
		sk_default_shader_ui          == nullptr ||
		sk_default_shader_ui_box      == nullptr ||
		sk_default_shader_ui_quadrant == nullptr ||
		sk_default_shader_ui_aura     == nullptr ||
		sk_default_shader_sky         == nullptr ||
		sk_default_shader_lines       == nullptr)
		return false;

	shader_set_id(sk_default_shader,             default_id_shader);
	shader_set_id(sk_default_shader_blit,        default_id_shader_blit);
	shader_set_id(sk_default_shader_pbr,         default_id_shader_pbr);
	shader_set_id(sk_default_shader_pbr_clip,    default_id_shader_pbr_clip);
	shader_set_id(sk_default_shader_unlit,       default_id_shader_unlit);
	shader_set_id(sk_default_shader_unlit_clip,  default_id_shader_unlit_clip);
	shader_set_id(sk_default_shader_lightmap,    default_id_shader_lightmap);
	shader_set_id(sk_default_shader_font,        default_id_shader_font);
	shader_set_id(sk_default_shader_equirect,    default_id_shader_equirect);
	shader_set_id(sk_default_shader_ui,          default_id_shader_ui);
	shader_set_id(sk_default_shader_ui_box,      default_id_shader_ui_box);
	shader_set_id(sk_default_shader_ui_quadrant, default_id_shader_ui_quadrant);
	shader_set_id(sk_default_shader_ui_aura,     default_id_shader_ui_aura);
	shader_set_id(sk_default_shader_sky,         default_id_shader_sky);
	shader_set_id(sk_default_shader_lines,       default_id_shader_lines);

	// Materials
	sk_default_material             = material_create(sk_default_shader);
	sk_default_material_pbr         = material_create(sk_default_shader_pbr);
	sk_default_material_pbr_clip    = material_create(sk_default_shader_pbr_clip);
	sk_default_material_unlit       = material_create(sk_default_shader_unlit);
	sk_default_material_unlit_clip  = material_create(sk_default_shader_unlit_clip);
	sk_default_material_equirect    = material_create(sk_default_shader_equirect);
	sk_default_material_font        = material_create(sk_default_shader_font);
	sk_default_material_ui          = material_create(sk_default_shader_ui);
	sk_default_material_ui_box      = material_create(sk_default_shader_ui_box);
	sk_default_material_ui_quadrant = material_create(sk_default_shader_ui_quadrant);
	sk_default_material_ui_aura     = material_create(sk_default_shader_ui_aura);

	if (sk_default_material             == nullptr ||
		sk_default_material_pbr         == nullptr ||
		sk_default_material_pbr_clip    == nullptr ||
		sk_default_material_unlit       == nullptr ||
		sk_default_material_unlit_clip  == nullptr ||
		sk_default_material_equirect    == nullptr ||
		sk_default_material_font        == nullptr ||
		sk_default_material_ui          == nullptr ||
		sk_default_material_ui_box      == nullptr ||
		sk_default_material_ui_quadrant == nullptr ||
		sk_default_material_ui_aura     == nullptr)
		return false;

	material_set_id(sk_default_material,             default_id_material);
	material_set_id(sk_default_material_pbr,         default_id_material_pbr);
	material_set_id(sk_default_material_pbr_clip,    default_id_material_pbr_clip);
	material_set_id(sk_default_material_unlit,       default_id_material_unlit);
	material_set_id(sk_default_material_unlit_clip,  default_id_material_unlit_clip);
	material_set_id(sk_default_material_equirect,    default_id_material_equirect);
	material_set_id(sk_default_material_font,        default_id_material_font);
	material_set_id(sk_default_material_ui,          default_id_material_ui);
	material_set_id(sk_default_material_ui_box,      default_id_material_ui_box);
	material_set_id(sk_default_material_ui_quadrant, default_id_material_ui_quadrant);
	material_set_id(sk_default_material_ui_aura,     default_id_material_ui_aura);

	material_set_texture     (sk_default_material_font, "diffuse", sk_default_tex);
	material_set_cull        (sk_default_material_ui_box, cull_none);
	material_set_transparency(sk_default_material_ui_box, transparency_msaa);

	// Text!
	sk_default_font = platform_default_font();
	if (sk_default_font == nullptr)
		return false;
	font_set_id(sk_default_font, default_id_font);
	sk_default_text_style = text_make_style_mat(sk_default_font, 20 * mm2m, sk_default_material_font, color128{ 1,1,1,1 });

	// Sounds
	sk_default_click = sound_generate([](float t){
		float x = t / 0.03f;
		float band1 = sinf(t*7500) * (x * powf(1 - x, 10)) / 0.03f;
		float band2 = sinf(t*4750) * (x * powf(1 - x, 12)) / 0.03f;
		float band3 = sinf(t*2500) * (x * powf(1 - x, 12)) / 0.03f;
		float band4 = sinf(t*500)  * (x * powf(1 - x, 6))  / 0.03f;
		float silencer = fmaxf(0,fminf(1,(0.03f-t)*200));

		return (band1*0.6f + band2*0.2f + band3*0.1f + band4*0.1f) * 0.2f * silencer;
		}, .08f);
	sk_default_unclick = sound_generate([](float t){
		float x = t / 0.03f;
		float band1 = sinf(t*7500) * (x * powf(1 - x, 10)) / 0.03f;
		float band2 = sinf(t*4750) * (x * powf(1 - x, 12)) / 0.03f;
		float band3 = sinf(t*2500) * (x * powf(1 - x, 12)) / 0.03f;
		float band4 = sinf(t*500)  * (x * powf(1 - x, 6))  / 0.03f;

		return (band1*0.2f + band2*0.4f + band3*0.1f + band4*0.1f) * 0.2f;
		}, .03f);
	sk_default_grab = sound_generate([](float t){
		float x = t / 0.04f;
		float band1 = sinf(t*4000*3.14f) * (x * powf(1 - x, 10)) / 0.04f;
		float band2 = sinf(t*6000*3.14f) * (x * powf(1 - x, 12)) / 0.04f;
		float band3 = sinf(t*1000*3.14f) * (x * powf(1 - x, 12)) / 0.04f;
		float band4 = sinf(t*400*3.14f)  * (x * powf(1 - x, 6))  / 0.04f;

		return (band1*0.05f + band2*0.05f + band3*0.2f + band4*0.7f) * 0.1f;
		}, .04f);
	sk_default_ungrab = sound_generate([](float t){
		float x = t / 0.04f;
		float band1 = sinf(t*3000*3.14f) * (x * powf(1 - x, 10)) / 0.04f;
		float band2 = sinf(t*5000*3.14f) * (x * powf(1 - x, 12)) / 0.04f;
		float band3 = sinf(t*800*3.14f) * (x * powf(1 - x, 12)) / 0.04f;
		float band4 = sinf(t*300*3.14f)  * (x * powf(1 - x, 6))  / 0.04f;

		return (band1*0.05f + band2*0.05f + band3*0.1f + band4*0.8f) * 0.1f;
		}, .04f);

	sound_set_id(sk_default_click,   default_id_sound_click);
	sound_set_id(sk_default_unclick, default_id_sound_unclick);
	sound_set_id(sk_default_grab,    default_id_sound_grab);
	sound_set_id(sk_default_ungrab,  default_id_sound_ungrab);

	return true;
}

///////////////////////////////////////////

void defaults_shutdown() {
	tex_set_error_fallback  (nullptr);
	tex_set_loading_fallback(nullptr);

	sound_release   (sk_default_click);
	sound_release   (sk_default_unclick);
	sound_release   (sk_default_grab);
	sound_release   (sk_default_ungrab);
	font_release    (sk_default_font);
	material_release(sk_default_material);
	material_release(sk_default_material_pbr);
	material_release(sk_default_material_pbr_clip);
	material_release(sk_default_material_unlit);
	material_release(sk_default_material_unlit_clip);
	material_release(sk_default_material_equirect);
	material_release(sk_default_material_font);
	material_release(sk_default_material_ui);
	material_release(sk_default_material_ui_box);
	material_release(sk_default_material_ui_quadrant);
	material_release(sk_default_material_ui_aura);
	shader_release  (sk_default_shader);
	shader_release  (sk_default_shader_blit);
	shader_release  (sk_default_shader_unlit);
	shader_release  (sk_default_shader_unlit_clip);
	shader_release  (sk_default_shader_lightmap);
	shader_release  (sk_default_shader_font);
	shader_release  (sk_default_shader_equirect);
	shader_release  (sk_default_shader_ui);
	shader_release  (sk_default_shader_ui_box);
	shader_release  (sk_default_shader_ui_quadrant);
	shader_release  (sk_default_shader_ui_aura);
	shader_release  (sk_default_shader_sky);
	shader_release  (sk_default_shader_lines);
	shader_release  (sk_default_shader_pbr);
	shader_release  (sk_default_shader_pbr_clip);
	mesh_release    (sk_default_cube);
	mesh_release    (sk_default_sphere);
	mesh_release    (sk_default_quad);
	mesh_release    (sk_default_screen_quad);
	tex_release     (sk_default_tex);
	tex_release     (sk_default_tex_black);
	tex_release     (sk_default_tex_gray);
	tex_release     (sk_default_tex_flat);
	tex_release     (sk_default_tex_rough);
	tex_release     (sk_default_tex_devtex);
	tex_release     (sk_default_tex_error);
	tex_release     (sk_default_cubemap);
}

} // namespace sk
